home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 001-025 / disk_010 / iff / iffw.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  8KB  |  217 lines

  1. /*----------------------------------------------------------------------*
  2.  * IFFW.C  Support routines for writing IFF-85 files.          12/02/85
  3.  * (IFF is Interchange Format File.)
  4.  *
  5.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  6.  * This software is in the public domain.
  7.  *
  8.  * This version for the Commodore-Amiga computer.
  9.  *----------------------------------------------------------------------*/
  10. #include "DF1:iff/iff.h"
  11. #include "DF1:iff/gio.h"
  12.  
  13. /* ---------- IFF Writer -----------------------------------------------*/
  14.  
  15. /* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
  16. #define Known(size)   ( (size) != szNotYetKnown )
  17.  
  18. /* Yet another weird macro to make the source code simpler...*/
  19. #define IfIffp(expr)  {if (iffp == IFF_OKAY)  iffp = (expr);}
  20.  
  21. /* ---------- OpenWIFF -------------------------------------------------*/
  22.  
  23. IFFP OpenWIFF(file, new0, limit)  BPTR file; GroupContext *new0; LONG limit; {
  24.     register GroupContext *new = new0;
  25.     register IFFP iffp = IFF_OKAY;
  26.  
  27.     new->parent       = NULL;
  28.     new->clientFrame  = NULL;
  29.     new->file         = file;
  30.     new->position     = 0;
  31.     new->bound        = limit;
  32.     new->ckHdr.ckID   = NULL_CHUNK;  /* indicates no current chunk */
  33.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  34.  
  35.     if (0 > Seek(file, 0, OFFSET_BEGINNING))   /* Go to start of the file.*/
  36.    iffp = DOS_ERROR;
  37.     else if ( Known(limit) && IS_ODD(limit) )
  38.    iffp = CLIENT_ERROR;
  39.     return(iffp);
  40.     }
  41.  
  42. /* ---------- StartWGroup ----------------------------------------------*/
  43. IFFP StartWGroup(parent, groupType, groupSize, subtype, new)
  44.       GroupContext *parent, *new; ID groupType, subtype; LONG groupSize;  {
  45.     register IFFP iffp;
  46.  
  47.     iffp = PutCkHdr(parent, groupType, groupSize);
  48.     IfIffp( IFFWriteBytes(parent, (BYTE *)&subtype, sizeof(ID)) );
  49.     IfIffp( OpenWGroup(parent, new) );
  50.     return(iffp);
  51.     }
  52.  
  53. /* ---------- OpenWGroup -----------------------------------------------*/
  54. IFFP OpenWGroup(parent0, new0)  GroupContext *parent0, *new0; {
  55.     register GroupContext *parent = parent0;
  56.     register GroupContext *new    = new0;
  57.     register LONG ckEnd;
  58.     register IFFP iffp = IFF_OKAY;
  59.  
  60.     new->parent       = parent;
  61.     new->clientFrame  = parent->clientFrame;
  62.     new->file         = parent->file;
  63.     new->position     = parent->position;
  64.     new->bound        = parent->bound;
  65.     new->ckHdr.ckID   = NULL_CHUNK;
  66.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  67.  
  68.     if ( Known(parent->ckHdr.ckSize) ) {
  69.    ckEnd = new->position + ChunkMoreBytes(parent);
  70.    if ( new->bound == szNotYetKnown || new->bound > ckEnd )
  71.        new->bound = ckEnd;
  72.    };
  73.  
  74.     if ( parent->ckHdr.ckID == NULL_CHUNK || /* not currently writing a chunk*/
  75.     IS_ODD(new->position) ||
  76.          (Known(new->bound) && IS_ODD(new->bound)) )
  77.    iffp = CLIENT_ERROR;
  78.     return(iffp);
  79.     }
  80.  
  81. /* ---------- CloseWGroup ----------------------------------------------*/
  82. IFFP CloseWGroup(old0)  GroupContext *old0; {
  83.     register GroupContext *old = old0;
  84.  
  85.     if ( old->ckHdr.ckID != NULL_CHUNK )  /* didn't close the last chunk */
  86.    return(CLIENT_ERROR);
  87.     if ( old->parent == NULL ) {     /* top level file context */
  88.    GWriteFlush(old->file);
  89.    }
  90.     else {
  91.    old->parent->bytesSoFar += old->position - old->parent->position;
  92.    old->parent->position = old->position;
  93.    };
  94.     return(IFF_OKAY);
  95.     }
  96.  
  97. /* ---------- EndWGroup ------------------------------------------------*/
  98. IFFP EndWGroup(old)  GroupContext *old;  {
  99.     register GroupContext *parent = old->parent;
  100.     register IFFP iffp;
  101.  
  102.     iffp = CloseWGroup(old);
  103.     IfIffp( PutCkEnd(parent) );
  104.     return(iffp);
  105.     }
  106.  
  107. /* ---------- PutCk ----------------------------------------------------*/
  108. IFFP PutCk(context, ckID, ckSize, data)
  109.       GroupContext *context; ID ckID; LONG ckSize; BYTE *data; {
  110.     register IFFP iffp = IFF_OKAY;
  111.  
  112.     if ( ckSize == szNotYetKnown )
  113.    iffp = CLIENT_ERROR;
  114.     IfIffp( PutCkHdr(context, ckID, ckSize) );
  115.     IfIffp( IFFWriteBytes(context, data, ckSize) );
  116.     IfIffp( PutCkEnd(context) );
  117.     return(iffp);
  118.     }
  119.  
  120. /* ---------- PutCkHdr -------------------------------------------------*/
  121. IFFP PutCkHdr(context0, ckID, ckSize)
  122.       GroupContext *context0;  ID ckID;  LONG ckSize; {
  123.     register GroupContext *context = context0;
  124.     LONG minPSize = sizeof(ChunkHeader); /* physical chunk >= minPSize bytes*/
  125.  
  126.     /* CLIENT_ERROR if we're already inside a chunk or asked to write
  127.      * other than one FORM, LIST, or CAT at the top level of a file */
  128.     /* Also, non-positive ID values are illegal and used for error codes.*/
  129.     /* (We could check for other illegal IDs...)*/
  130.     if ( context->ckHdr.ckID != NULL_CHUNK  ||  ckID <= 0 )
  131.    return(CLIENT_ERROR);
  132.     else if (context->parent == NULL)  {
  133.    switch (ckID)  {
  134.        case FORM:  case LIST:  case CAT:  break;
  135.        default: return(CLIENT_ERROR);
  136.        }
  137.    if (context->position != 0)
  138.        return(CLIENT_ERROR);
  139.    }
  140.  
  141.     if ( Known(ckSize) ) {
  142.    if ( ckSize < 0 )
  143.        return(CLIENT_ERROR);
  144.    minPSize += ckSize;
  145.    };
  146.     if ( Known(context->bound)  &&
  147.          context->position + minPSize > context->bound )
  148.    return(CLIENT_ERROR);
  149.  
  150.     context->ckHdr.ckID   = ckID;
  151.     context->ckHdr.ckSize = ckSize;
  152.     context->bytesSoFar   = 0;
  153.     if (0 > GWrite(context->file, &context->ckHdr, sizeof(ChunkHeader)))
  154.    return(DOS_ERROR);
  155.     context->position += sizeof(ChunkHeader);
  156.     return(IFF_OKAY);
  157.     }
  158.  
  159. /* ---------- IFFWriteBytes ---------------------------------------------*/
  160. IFFP IFFWriteBytes(context0, data, nBytes)
  161.       GroupContext *context0;  BYTE *data;  LONG nBytes; {
  162.     register GroupContext *context = context0;
  163.  
  164.     if ( context->ckHdr.ckID == NULL_CHUNK  ||   /* not in a chunk */
  165.     nBytes < 0  ||            /* negative nBytes */
  166.     (Known(context->bound)  &&      /* overflow context */
  167.        context->position + nBytes > context->bound)  ||
  168.     (Known(context->ckHdr.ckSize)  &&      /* overflow chunk */
  169.        context->bytesSoFar + nBytes > context->ckHdr.ckSize) )
  170.    return(CLIENT_ERROR);
  171.  
  172.     if (0 > GWrite(context->file, data, nBytes))
  173.    return(DOS_ERROR);
  174.  
  175.     context->bytesSoFar += nBytes;
  176.     context->position   += nBytes;
  177.     return(IFF_OKAY);
  178.     }
  179.  
  180. /* ---------- PutCkEnd -------------------------------------------------*/
  181. IFFP PutCkEnd(context0)  GroupContext *context0; {
  182.     register GroupContext *context = context0;
  183.     WORD zero = 0;   /* padding source */
  184.  
  185.     if ( context->ckHdr.ckID == NULL_CHUNK )  /* not in a chunk */
  186.    return(CLIENT_ERROR);
  187.  
  188.     if ( context->ckHdr.ckSize == szNotYetKnown ) {
  189.    /* go back and set the chunk size to bytesSoFar */
  190.    if ( 0 > GSeek(context->file,
  191.             -(context->bytesSoFar + sizeof(LONG)),
  192.             OFFSET_CURRENT)  ||
  193.         0 > GWrite(context->file, &context->bytesSoFar, sizeof(LONG))  ||
  194.         0 > GSeek(context->file, context->bytesSoFar, OFFSET_CURRENT)  )
  195.        return(DOS_ERROR);
  196.    }
  197.     else {  /* make sure the client wrote as many bytes as planned */
  198.    if ( context->ckHdr.ckSize != context->bytesSoFar )
  199.        return(CLIENT_ERROR);
  200.    };
  201.  
  202.     /* Write a pad byte if needed to bring us up to an even boundary.
  203.      * Since the context end must be even, and since we haven't
  204.      * overwritten the context, if we're on an odd position there must
  205.      * be room for a pad byte. */
  206.     if ( IS_ODD(context->bytesSoFar) ) {
  207.    if ( 0 > GWrite(context->file, &zero, 1) )
  208.        return(DOS_ERROR);
  209.    context->position += 1;
  210.    };
  211.  
  212.     context->ckHdr.ckID   = NULL_CHUNK;
  213.     context->ckHdr.ckSize = context->bytesSoFar = 0;
  214.     return(IFF_OKAY);
  215.     }
  216.  
  217.